/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ajustecurvas.controller;

import ajustecurvas.Ponto;
import java.text.DecimalFormat;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 *
 * @author Andre, Charlene, Mayara
 */
public class HiperbolicoAjusteController extends AjusteController {

    private String equacao = "";
    private double qualidade = 0;
    

    @Override
    public List<Ponto> calcular(List<Ponto> medidas) {

        double somaX = 0;     // somatorio de x
        double somaY = 0;     // somatorio de y
        double somaY2 = 0;    // somatorio de y²
        double umSobreX = 0;  // somatorio de 1/x
        double umSobreX2 = 0; // somatorio de 1/x²
        double ySobreX = 0;   // somatorio de y/x

        // Realiza os somatórios necessários
        for (int i = 0; i < medidas.size(); i++) {

            somaX = somaX + medidas.get(i).getX();
            somaY = somaY + medidas.get(i).getY();
            somaY2 = somaY2 + Math.pow(medidas.get(i).getY(), 2);
            umSobreX = umSobreX + (1 / medidas.get(i).getX());
            umSobreX2 = umSobreX2 + Math.pow(1 / medidas.get(i).getX(), 2);
            ySobreX = ySobreX + (medidas.get(i).getY() / medidas.get(i).getX());

        }

        // encontrando os valors de a e b para y = a + b/x
        // o sistema de equações 2x2 sempre será: 
                      /*   |  n  + 1/x |  |a|  = |y  |          
                           | 1/x + 1/x²|  |b|  = |y/x|    */
        double matrizApliada [][] = {{medidas.size(), umSobreX, somaY},
                                     {umSobreX, umSobreX2, ySobreX}};
        
        double rSL[] = eliminacaoGaussiana(matrizApliada);
        gerarEquacao(rSL);
        
        List<Ponto> pontosAjustados = gerarPontos(rSL, medidas);  
        
        gerarQualidade(rSL, medidas, pontosAjustados, somaY, somaY2);
  
        return pontosAjustados;
    }

    @Override
    public String getEquacao() {
        return equacao;
    }

    @Override
    public double getQualidade() {
        return qualidade;
    }

    /**
     * Esse método resolve um sistema matricial do tipo Ax = b.
     *
     * @return
     */
    private double[] eliminacaoGaussiana(double matrizAmpliada[][]) {

        double[] resposta = new double[matrizAmpliada.length];

        // Este laço torna a matriz original na matriz triangular superior
        for (int k = 0; k < matrizAmpliada.length; k++) {

            for (int i = k + 1; i < matrizAmpliada.length; i++) {
                //PASSO 1 - ENCONTRAR O FATOR MULTIPLICATIVO * Mik = Aik/Akk
                double fatorMultiplicativo = matrizAmpliada[i][k] / matrizAmpliada[k][k];
                
                // PASSO 2 - ZERAR A POSIÇAO DA MATRIZ * Aij = Aij - Mik*Akj
                for (int j = k; j < matrizAmpliada[0].length; j++) {
                    matrizAmpliada[i][j] = matrizAmpliada[i][j] - fatorMultiplicativo * matrizAmpliada[k][j];
                }
            }

        }

        //Este laço executa o processo de substituição retroativa
        for (int i = resposta.length - 1;
                i >= 0; i--) {

            if (i == resposta.length - 1) {
                // Caso direto: Xn = Bn/Unn
                resposta[i] = matrizAmpliada[i][matrizAmpliada[0].length - 1] / matrizAmpliada[i][i];
            } else {
                // Demais casos: Xi = (Bi - Somatorio(Uij*Xj))/Uii
                double somatorio = 0;
                for (int j = i + 1; j < matrizAmpliada.length; j++) {
                    somatorio = somatorio + matrizAmpliada[i][j] * resposta[j];
                }
                resposta[i] = (matrizAmpliada[i][matrizAmpliada[0].length - 1] - somatorio) / matrizAmpliada[i][i];
            }

        }
        return resposta;
    }
    
    /**
     * Recebe como parametro a resposta do sistema de equações lineares
     * e monta a String da equação de melhor ajuste.
     * 
     * @param resposta 
     */
    private void gerarEquacao(double resposta[]){
        DecimalFormat df = new DecimalFormat("0.00");  
        // y = a + b/x
        equacao =  df.format(resposta[0]) + "  " + df.format(resposta[1]) + "/x";       
    }
    
    /**
     * Gera os pontos ajustados para os mesmos valores x da função original
     * 
     * @param ab valores de a e b da equação "y = a + b/x"
     * @param medidas conjunto de pontos originais
     * @return 
     */
    private List<Ponto> gerarPontos(double ab[], List<Ponto> medidas){
         
        ArrayList<Ponto> pontos = new ArrayList<Ponto>();
         
        for(int i=0; i < medidas.size(); i++){
            
            double y = ab[0] + (ab[1]/medidas.get(i).getX());
            
            pontos.add(new Ponto(medidas.get(i).getX(),y));
            
        } 
         
         return pontos;
    }
    
  /**
   * Calcula o coeficiente de qualidade do ajuste hiperbólico
   * 
   * @param rSL resposta do sistema linear
   * @param medidas conjunto de pontos originais
   * @param ajuste conjunto de pontos ajustados
   * @param somaY somatorio de y
   * @param somaY2 somatorio de y²
   */
    private void gerarQualidade(double[] rSL, List<Ponto> medidas ,List<Ponto> ajuste, double somaY, double somaY2){
        
        double numerador =0;
        
        for(int i=0; i < ajuste.size(); i++ ){
            numerador += Math.pow( (medidas.get(i).getY() - ajuste.get(i).getY()), 2);
        }
        
        qualidade = 1 -((ajuste.size()*numerador)/((ajuste.size()*somaY2) - (Math.pow(somaY, 2))));
    }
}
